package com.quectel.business.common.scheduling;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.func.LambdaUtil;
import com.google.common.base.Preconditions;
import com.quectel.base.tuple.Tuple2;
import com.quectel.base.tuple.Tuples;
import com.quectel.constant.core.scheduling.SchedulingApplyConstants;
import com.quectel.constant.core.scheduling.SchedulingLeaveConstants;
import com.quectel.constant.global.SystemConstants;
import com.quectel.core.module.dept.dto.DeptPersonDto;
import com.quectel.core.module.dept.service.DeptPersonService;
import com.quectel.core.module.scheduling.dto.SchedulingLeaveBalanceDto;
import com.quectel.core.module.scheduling.dto.SchedulingLeaveConfigDto;
import com.quectel.core.module.scheduling.dto.SchedulingLeavePersonBindingDto;
import com.quectel.core.module.scheduling.service.SchedulingAttendanceGroupService;
import com.quectel.core.module.scheduling.service.SchedulingLeaveBalanceService;
import com.quectel.core.module.scheduling.service.SchedulingLeaveConfigService;
import com.quectel.core.module.scheduling.service.SchedulingLeavePersonBindingService;
import com.quectel.core.module.schedulingapply.dto.SchedulingApplyLeaveRecordDto;
import com.quectel.core.module.schedulingapply.dto.vo.SchedulingBalanceTimeVO;
import com.quectel.core.module.schedulingapply.dto.vo.SchedulingTimeVO;
import com.quectel.core.module.schedulingapply.service.SchedulingApplyLeaveRecordService;
import com.quectel.util.common.CopyUtils;
import com.quectel.util.common.DateUtils;
import com.quectel.util.common.Response;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author zhangheng
 * @email nolan.zhang@quectel.com
 * @date 2023/7/10 14:29
 * @description 假期相关
 */
@Component
@Slf4j
public class SchedulingLeaveBusiness {

    @DubboReference
    private SchedulingAttendanceGroupService schedulingAttendanceGroupService;
    @DubboReference
    private SchedulingLeaveConfigService schedulingLeaveConfigService;
    @DubboReference
    private SchedulingLeaveBalanceService schedulingLeaveBalanceService;
    @DubboReference
    private DeptPersonService deptPersonService;
    @DubboReference
    private SchedulingLeavePersonBindingService schedulingLeavePersonBindingService;
    @DubboReference
    private SchedulingApplyLeaveRecordService schedulingApplyLeaveRecordService;

    public List<SchedulingApplyLeaveRecordDto> getLeaveRecord(Long tenantId, Long villageId, Long leaveId, Long personId, String startTime, String endTime) {
        Map<String, Object> params = new HashMap<>();
        params.put(LambdaUtil.getFieldName(SchedulingApplyLeaveRecordDto::getTenantId), tenantId);
        params.put(LambdaUtil.getFieldName(SchedulingApplyLeaveRecordDto::getVillageId), villageId);
        params.put(LambdaUtil.getFieldName(SchedulingApplyLeaveRecordDto::getLeaveId), leaveId);
        params.put(LambdaUtil.getFieldName(SchedulingApplyLeaveRecordDto::getApplyDeptPersonId), personId);
        params.put(LambdaUtil.getFieldName(SchedulingApplyLeaveRecordDto::getStartTime), startTime);
        params.put(LambdaUtil.getFieldName(SchedulingApplyLeaveRecordDto::getEndTime), endTime);

        return schedulingApplyLeaveRecordService.queryList(params);
    }

    /**
     * 判断时间是否有交叉,如果有交叉则不让保存
     *
     * @param leaveId   假期id
     * @param personId  员工id
     * @param year      年份
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return 如果交叉返回true 否则返回false
     */
    public boolean isTimeCrossed(Long leaveId, Long personId, Integer year, String startTime, String endTime) {
        //查询同一时期是否有其他生效的余额
        Map<String, Object> balanceParams = new HashMap<>();
        balanceParams.put(LambdaUtil.getFieldName(SchedulingLeaveBalanceDto::getLeaveId), leaveId);
        balanceParams.put(LambdaUtil.getFieldName(SchedulingLeaveBalanceDto::getPersonId), personId);
        balanceParams.put(LambdaUtil.getFieldName(SchedulingLeaveBalanceDto::getYear), year);
        balanceParams.put(LambdaUtil.getFieldName(SchedulingLeaveBalanceDto::getStartTime), startTime);
        balanceParams.put(LambdaUtil.getFieldName(SchedulingLeaveBalanceDto::getEndTime), endTime);
        balanceParams.put(SchedulingLeaveConstants.IS_TIME_CROSSED, SystemConstants.YES);
        int i = schedulingLeaveBalanceService.queryTotal(balanceParams);
        return i > 0;
    }

    /**
     * 获取小区限额假期列表
     *
     * @param villageId 小区id
     * @return 限额假期列表
     */

    public List<SchedulingLeaveConfigDto> getSchedulingLeaveBalanceRep(Long tenantId, Long villageId) {
        Map<String, Object> leaveParams = new HashMap<>();
        leaveParams.put(LambdaUtil.getFieldName(SchedulingLeaveConfigDto::getTenantId), tenantId);
        leaveParams.put(LambdaUtil.getFieldName(SchedulingLeaveConfigDto::getVillageId), villageId);
        leaveParams.put(LambdaUtil.getFieldName(SchedulingLeaveConfigDto::getLimitFlag), SystemConstants.YES);
        leaveParams.put(LambdaUtil.getFieldName(SchedulingLeaveConfigDto::getStatus), SystemConstants.NORMAL);

        return schedulingLeaveConfigService.queryList(leaveParams);

    }


    /**
     * 假期配置校验
     *
     * @param leaveDto 请假配置
     * @return 校验结果
     */
    public Response checkLeaveConfig(SchedulingLeaveConfigDto leaveDto) {
        if (leaveDto == null) {
            return Response.error("该假期类型不存在，请联系管理员");
        }
        if (SystemConstants.ABNORMAL.equals(leaveDto.getStatus())) {
            return Response.error("该假期类型不可用，请联系管理员");
        }
        if (SystemConstants.YES.equals(leaveDto.getLimitFlag()) && leaveDto.getGrantRule() == null) {
            return Response.error("该假期配置错误，请联系管理员");
        }
        if (leaveDto.getUsageRule() == null) {
            return Response.error("该假期配置错误，请联系管理员");
        }
        SchedulingLeaveConstants.TimeUnitEnum leaveMinUnit = SchedulingLeaveConstants.TimeUnitEnum.getByUnit(leaveDto.getMinUnit());
        if (leaveMinUnit == null) {
            return Response.error("假期配置有误，请联系管理员");
        }
//        SchedulingLeaveConstants.CalLeaveTimeLenghTypeEnum calLeaveTimeLenghTypeEnum = SchedulingLeaveConstants.CalLeaveTimeLenghTypeEnum.getByType(leaveDto.getCalTimeLengthType());
//        if (calLeaveTimeLenghTypeEnum == null) {
//            return Response.error("假期配置有误，请联系管理员");
//        }
        return Response.ok();
    }


    /**
     * 加班自动转调休余额
     *
     * @param personId         人员id
     * @param date             加班日期
     * @param schedulingTimeVO 加班时长
     * @return 更新结果
     */
    public Response leaveBalanceUpdate(Long personId, Date date, Long dailyId, SchedulingTimeVO schedulingTimeVO, Byte leaveType) {
        DeptPersonDto deptPersonDto = deptPersonService.selectCacheById(personId);
        if ((deptPersonDto == null) || deptPersonDto.getStatus().equals(SystemConstants.ABNORMAL)) {
            return Response.error("员工不存在或被冻结");
        }

        //1查询是否有对应的假期
        Map<String, Object> params = new HashMap<>(4);
        params.put(LambdaUtil.getFieldName(SchedulingLeaveConfigDto::getTenantId), deptPersonDto.getTenantId());
        params.put(LambdaUtil.getFieldName(SchedulingLeaveConfigDto::getVillageId), deptPersonDto.getVillageId());
        params.put(LambdaUtil.getFieldName(SchedulingLeaveConfigDto::getType), leaveType);
        params.put(LambdaUtil.getFieldName(SchedulingLeaveConfigDto::getStatus), SystemConstants.YES);
        params.put(LambdaUtil.getFieldName(SchedulingLeavePersonBindingDto::getDeptPersonId), personId);

        List<SchedulingLeavePersonBindingDto> schedulingLeavePersonBindingDtos = schedulingLeavePersonBindingService.queryLeaveList(params);

        if (CollectionUtils.isEmpty(schedulingLeavePersonBindingDtos)) {
            return Response.error("该员工没有对应的假期");
        }
        SchedulingLeavePersonBindingDto schedulingLeavePersonBindingDto = schedulingLeavePersonBindingDtos.get(0);

        SchedulingLeaveConfigDto schedulingLeaveConfigDto = schedulingLeaveConfigService.selectCacheById(schedulingLeavePersonBindingDto.getLeaveId());
        Response leaveCheckResponse = checkLeaveConfig(schedulingLeaveConfigDto);
        if (!leaveCheckResponse.isSuccess()) {
            return Response.error(leaveCheckResponse.getMessage());
        }

        //2.根据配置获取开始时间和结束时间  查询是否有对应的假期余额
        Response<Date> startTimeResult = getStartTime(schedulingLeaveConfigDto.getGrantRule(), date, deptPersonDto.getJoinedDate());
        if (!startTimeResult.isSuccess()) {
            return Response.error(startTimeResult.getMessage());
        }
        Response<Tuple2<Date, Date>> endTimeResult = getEndTime(schedulingLeaveConfigDto.getGrantRule(), startTimeResult.getData());
        if (!endTimeResult.isSuccess()) {
            return Response.error(endTimeResult.getMessage());
        }

        //3.查询对应范围的假期余额
        params.clear();
        params.put(LambdaUtil.getFieldName(SchedulingLeaveBalanceDto::getLeaveId), schedulingLeaveConfigDto.getId());
        params.put(LambdaUtil.getFieldName(SchedulingLeaveBalanceDto::getPersonId), personId);
        params.put(SchedulingLeaveConstants.EXACT_START_TIME, DateUtils.format(startTimeResult.getData(), DateUtils.FormatType.COMMON));
        params.put(SchedulingLeaveConstants.EXACT_END_TIME, DateUtils.format(endTimeResult.getData().getT1(), DateUtils.FormatType.COMMON));

        List<SchedulingLeaveBalanceDto> schedulingLeaveBalanceDtos = schedulingLeaveBalanceService.queryList(params);
        if (CollectionUtils.isNotEmpty(schedulingLeaveBalanceDtos)) {
            SchedulingLeaveBalanceDto schedulingLeaveBalanceDto = schedulingLeaveBalanceDtos.get(0);
            BigDecimal bigDecimal = changeUnit(schedulingTimeVO.getTimeLength(), schedulingTimeVO.getTimeUnit(), schedulingLeaveConfigDto.getMinUnit());
            schedulingLeaveBalanceService.updateBalance(
                    personId,
                    SchedulingApplyConstants.BalanceTypeEnum.OVERTIME_CONVERTED_TO_BALANCE.getType(),
                    dailyId,
                    schedulingLeaveBalanceDto.getId(),
                    schedulingLeaveBalanceDto.getBalance().add(bigDecimal),
                    schedulingLeaveConfigDto.getMinUnit()
            );
        } else {
            Response<SchedulingLeaveBalanceDto> schedulingLeaveBalanceDtoResponse = genByConfig(personId, schedulingLeaveConfigDto, date);
            if (!schedulingLeaveBalanceDtoResponse.isSuccess()) {
                return Response.error(schedulingLeaveBalanceDtoResponse.getMessage());
            }
            SchedulingLeaveBalanceDto data = schedulingLeaveBalanceDtoResponse.getData();
            BigDecimal bigDecimal = changeUnit(schedulingTimeVO.getTimeLength(), schedulingTimeVO.getTimeUnit(), schedulingLeaveConfigDto.getMinUnit());
            data.setBalance(BigDecimal.ZERO);
            data.setId(schedulingLeaveBalanceService.save(data));

            schedulingLeaveBalanceService.updateBalance(
                    personId,
                    SchedulingApplyConstants.BalanceTypeEnum.OVERTIME_CONVERTED_TO_BALANCE.getType(),
                    dailyId,
                    data.getId(),
                    bigDecimal,
                    schedulingLeaveConfigDto.getMinUnit()
            );
        }
        return Response.ok();
    }


    /**
     * 根据配置获取余额
     *
     * @param personId                 人员id
     * @param schedulingLeaveConfigDto 假期配置
     * @param date                     申请时间或者发放时间
     * @return 假期余额对象
     */
    public Response<SchedulingLeaveBalanceDto> genByConfig(Long personId, SchedulingLeaveConfigDto schedulingLeaveConfigDto, Date date) {
        DeptPersonDto deptPersonDto = deptPersonService.selectCacheById(personId);
        if (deptPersonDto.getFirstJobDate() == null || deptPersonDto.getJoinedDate() == null) {
            return Response.error("员工首次参加工作日期或者入职日期没有配置");
        }
        Response<Date> startTimeResult = getStartTime(schedulingLeaveConfigDto.getGrantRule(), date, deptPersonDto.getJoinedDate());
        if (!startTimeResult.isSuccess()) {
            return Response.error(startTimeResult.getMessage());
        }
        Response<Tuple2<Date, Date>> endTimeResult = getEndTime(schedulingLeaveConfigDto.getGrantRule(), startTimeResult.getData());
        if (!endTimeResult.isSuccess()) {
            return Response.error(endTimeResult.getMessage());
        }
        SchedulingLeaveBalanceDto schedulingLeaveBalanceDto = new SchedulingLeaveBalanceDto();
        schedulingLeaveBalanceDto.setLeaveId(schedulingLeaveConfigDto.getId());
        schedulingLeaveBalanceDto.setPersonId(personId);
        schedulingLeaveBalanceDto.setBalance(new BigDecimal("0"));
        schedulingLeaveBalanceDto.setUsedBalance(new BigDecimal("0"));
        schedulingLeaveBalanceDto.setUnit(schedulingLeaveConfigDto.getMinUnit());
        schedulingLeaveBalanceDto.setYear(DateUtil.year(startTimeResult.getData()));
        schedulingLeaveBalanceDto.setStartTime(startTimeResult.getData());
        schedulingLeaveBalanceDto.setEndTime(endTimeResult.getData().getT1());
        schedulingLeaveBalanceDto.setEffectiveStartTime(startTimeResult.getData());
        schedulingLeaveBalanceDto.setEffectiveEndTime(endTimeResult.getData().getT2());
        schedulingLeaveBalanceDto.setCreateTime(new Date());

        return Response.<SchedulingLeaveBalanceDto>ok().wrap(schedulingLeaveBalanceDto);
    }

    /**
     * 获取假期余额开始时间
     *
     * @param grantRule  发放规则
     * @param date       发放时间
     * @param joinedDate 入职时间
     * @return 假期余额开始时间
     */
    public Response<Date> getStartTime(SchedulingLeaveConfigDto.GrantRule grantRule, Date date, Date joinedDate) {
        Preconditions.checkNotNull(grantRule);
        Date startTime = null;

        if (SchedulingLeaveConstants.GrantMethodEnum.YEAR.getMethod().equals(grantRule.getMethod())) {
            //1.1按年发放只能是每年固定日期或者入职时间
            if (SchedulingLeaveConstants.GrantPeriodTypeEnum.CUSTOM_TIME.getType().equals(grantRule.getPeriodType())) {
                //1.1.1获取年份
                String year = String.valueOf(DateUtil.year(date));
                startTime = DateUtil.beginOfDay(DateUtils.parse(year + "-" + grantRule.getPeriod(), DateUtils.FormatType.SIMPLE));
            } else if (SchedulingLeaveConstants.GrantPeriodTypeEnum.ENTRY_TIME.getType().equals(grantRule.getPeriodType())) {
                startTime = DateUtil.beginOfDay(joinedDate);
            } else {
                return Response.error("发放规则发放时间配置有误");
            }
        } else if (SchedulingLeaveConstants.GrantMethodEnum.MONTH.getMethod().equals(grantRule.getMethod())) {
            //2.1按月发放是每月固定周期
            //2.2获取月份
            DateTime beginOfMonthDate = DateUtil.beginOfMonth(date);
            //发放周期如果是月则月份的时间是01~28
            startTime = DateUtil.offsetDay(beginOfMonthDate, Integer.parseInt(grantRule.getPeriod()) - 1);

        } else if (SchedulingLeaveConstants.GrantMethodEnum.AUTO_CONVERT.getMethod().equals(grantRule.getMethod())) {
            //3.1自动转余额需要看有效期时间类型,如果是年则是1月1号,如果是月则是每月第一天,如果是周,则是每周第一天
            if (SchedulingLeaveConstants.PeriodTimeUnitEnum.YEAR.getUnit().equals(grantRule.getEffectiveUnit())) {
                startTime = DateUtil.beginOfYear(date);
            } else if (SchedulingLeaveConstants.PeriodTimeUnitEnum.MONTH.getUnit().equals(grantRule.getEffectiveUnit())) {
                startTime = DateUtil.beginOfMonth(date);
            } else if (SchedulingLeaveConstants.PeriodTimeUnitEnum.WEEK.getUnit().equals(grantRule.getEffectiveUnit())) {
                startTime = DateUtil.beginOfWeek(date);
            } else if (SchedulingLeaveConstants.PeriodTimeUnitEnum.DAY.getUnit().equals(grantRule.getEffectiveUnit())) {
                startTime = DateUtil.beginOfDay(date);
            }
        }
        if (startTime == null) {
            return Response.error("无法获取开始日期");
        }
        if (date.before(startTime)) {
            return Response.error("还未到发放日期,发放日期是:" + DateUtils.format(startTime, DateUtils.FormatType.SIMPLE));
        }

        return Response.<Date>ok().wrap(startTime);
    }

//    //有效期开始结束日期测试
//    public static void main(String[] args) {
//        String grandRuleJson =
//                "{\n" +
//                        "    \"method\":1,\n" +
//                        "    \"periodType\":1,\n" +
//                        "    \"period\":\"01-01\",\n" +
//                        "    \"effectiveType\":1,\n" +
//                        "    \"effectiveNum\":1,\n" +
//                        "    \"effectiveUnit\":4,\n" +
//                        "    \"effectiveExtendNum\":0,\n" +
//                        "    \"effectiveExtendNumUnit\":1\n" +
//                        "}";
//        SchedulingLeaveConfigDto.GrantRule parse = JacksonUtils.parse(grandRuleJson, SchedulingLeaveConfigDto.GrantRule.class);
//        SchedulingLeaveBusiness schedulingLeaveBusiness = new
//                SchedulingLeaveBusiness();
//        Date date = DateUtils.parse("2023-01-01 00:00:00", DateUtils.FormatType.COMMON);
//        Date joinedDate = DateUtils.parse("2023-01-02 00:00:00", DateUtils.FormatType.COMMON);
//        Response<Date> startTime = schedulingLeaveBusiness.getStartTime(parse, date, joinedDate);
//        Response<Date> endTime = schedulingLeaveBusiness.getEndTime(parse, startTime.getData());
//        System.out.println(endTime);
//
//    }


    /**
     * 获取余额有效期开始时间
     *
     * @param grantRule 发放规则
     * @param startTime 开始时间
     * @return 第一个date是周期结束日期 第二个date是有效期截止日期
     */
    public Response<Tuple2<Date, Date>> getEndTime(SchedulingLeaveConfigDto.GrantRule grantRule, Date startTime) {
        Preconditions.checkNotNull(grantRule);
        Date endTime = null;
        Date effectiveEndTime = null;
        if (SchedulingLeaveConstants.EffectiveTypeEnum.ISSUE_DAY.getType().equals(grantRule.getEffectiveType())) {
            Date nextEffectiveDate = null;
            //1.1如果有效期是年
            if (SchedulingLeaveConstants.PeriodTimeUnitEnum.YEAR.getUnit().equals(grantRule.getEffectiveUnit())) {
                nextEffectiveDate = DateUtil.offsetMonth(startTime, grantRule.getEffectiveNum() * 12);
            } else if (SchedulingLeaveConstants.PeriodTimeUnitEnum.MONTH.getUnit().equals(grantRule.getEffectiveUnit())) {
                nextEffectiveDate = DateUtil.offsetMonth(startTime, grantRule.getEffectiveNum());
            } else if (SchedulingLeaveConstants.PeriodTimeUnitEnum.WEEK.getUnit().equals(grantRule.getEffectiveUnit())) {
                nextEffectiveDate = DateUtil.offsetWeek(startTime, grantRule.getEffectiveNum());
            } else if (SchedulingLeaveConstants.PeriodTimeUnitEnum.DAY.getUnit().equals(grantRule.getEffectiveUnit())) {
                nextEffectiveDate = DateUtil.offsetDay(startTime, grantRule.getEffectiveNum());
            }
            endTime = DateUtil.endOfDay(DateUtil.offsetDay(nextEffectiveDate, -1));
            //1.2超过有效期还有多长时间有效
            if (grantRule.getEffectiveExtendNum() != null && grantRule.getEffectiveExtendNum() > 0) {
                if (SchedulingLeaveConstants.PeriodTimeUnitEnum.DAY.getUnit().equals(grantRule.getEffectiveExtendNumUnit())) {
                    effectiveEndTime = DateUtil.offsetDay(nextEffectiveDate, grantRule.getEffectiveExtendNum());
                } else if (SchedulingLeaveConstants.PeriodTimeUnitEnum.WEEK.getUnit().equals(grantRule.getEffectiveExtendNumUnit())) {
                    effectiveEndTime = DateUtil.offsetWeek(nextEffectiveDate, grantRule.getEffectiveExtendNum());
                } else if (SchedulingLeaveConstants.PeriodTimeUnitEnum.MONTH.getUnit().equals(grantRule.getEffectiveExtendNumUnit())) {
                    effectiveEndTime = DateUtil.offsetMonth(nextEffectiveDate, grantRule.getEffectiveExtendNum());
                }
            }
            //1.3扣除一天
            effectiveEndTime = DateUtil.endOfDay(DateUtil.offsetDay(effectiveEndTime, -1));

        } else if (SchedulingLeaveConstants.EffectiveTypeEnum.NATURE.getType().equals(grantRule.getEffectiveType())) {
            //2.1当年有效
            if (SchedulingLeaveConstants.PeriodTimeUnitEnum.YEAR.getUnit().equals(grantRule.getEffectiveUnit())) {
                endTime = DateUtil.endOfYear(startTime);
            } else if (SchedulingLeaveConstants.PeriodTimeUnitEnum.MONTH.getUnit().equals(grantRule.getEffectiveUnit())) {
                endTime = DateUtil.endOfMonth(startTime);
            } else if (SchedulingLeaveConstants.PeriodTimeUnitEnum.WEEK.getUnit().equals(grantRule.getEffectiveUnit())) {
                endTime = DateUtil.endOfWeek(startTime);
            } else if (SchedulingLeaveConstants.PeriodTimeUnitEnum.DAY.getUnit().equals(grantRule.getEffectiveUnit())) {
                endTime = DateUtil.endOfDay(startTime);
            }
            //2.2有效期截止日期和结束日期一致
            effectiveEndTime = endTime;
        } else if (SchedulingLeaveConstants.EffectiveTypeEnum.PERMANENT.getType().equals(grantRule.getEffectiveType())) {
            endTime = DateUtils.parse(SchedulingLeaveConstants.LONG_TERM_TIME, DateUtils.FormatType.COMMON);
            //2.3有效期截止日期和结束日期一致
            effectiveEndTime = endTime;
        }
        if (endTime == null) {
            return Response.error("获取结束日期为空");
        }

        return Response.<Tuple2<Date, Date>>ok().wrap(Tuples.of(endTime, effectiveEndTime));
    }

    //有效期结束日期测试
//    public static void main(String[] args) {
//        String grandRuleJson = "" +
//                "{\n" +
//                "    \"method\":1,\n" +
//                "    \"periodType\":1,\n" +
//                "    \"period\":\"01-01\",\n" +
//                "    \"effectiveType\":1,\n" +
//                "    \"effectiveNum\":1,\n" +
//                "    \"effectiveUnit\":4,\n" +
//                "    \"effectiveExtendNum\":1,\n" +
//                "    \"effectiveExtendNumUnit\":3\n" +
//                "}";
//        SchedulingLeaveConfigDto.GrantRule parse = JacksonUtils.parse(grandRuleJson, SchedulingLeaveConfigDto.GrantRule.class);
//        SchedulingLeaveBusiness schedulingLeaveBusiness = new
//                SchedulingLeaveBusiness();
//        Date startDate = DateUtils.parse("2023-01-01 00:00:00", DateUtils.FormatType.COMMON);
//        Response<Date> endTime = schedulingLeaveBusiness.getEndTime(parse, startDate);
//        System.out.println(endTime.getData());
//
//
//    }

    /**
     * 获取发放余额
     *
     * @param grantRule    授权规则
     * @param joinedDate   入职时间
     * @param firstJobDate 首次工作日期
     * @return 获取距离现在的时长, 单位是天
     */
    public Response<SchedulingTimeVO> getGrantBalanceByCombinedGrant(Byte unit, SchedulingLeaveConfigDto.GrantRule grantRule, Date joinedDate, Date firstJobDate) {
        Preconditions.checkNotNull(grantRule);
        Preconditions.checkNotNull(grantRule.getCombinedGrant());
        Date now = new Date();

        SchedulingTimeVO schedulingTimeVO = new SchedulingTimeVO();
        schedulingTimeVO.setTimeLength(BigDecimal.ZERO);
        schedulingTimeVO.setTimeUnit(unit);

        if (grantRule.getGrantStandard().equals(SchedulingLeaveConstants.GrantStandardEnum.FIXED.getType())) {
            schedulingTimeVO.setTimeLength(BigDecimal.valueOf(grantRule.getCombinedGrant().getFixedAmount()));
        } else {
            //1.1获取司龄
            long companyAge = DateUtil.betweenYear(joinedDate, now, true);
            //1.2.获取工龄
            long workAge = DateUtil.betweenYear(firstJobDate, now, true);

            SchedulingLeaveConfigDto.CombinedGrant combinedGrant = grantRule.getCombinedGrant();
            //1.3.分别的获取发放数量
            BigDecimal companySchedulingTime = BigDecimal.ZERO;
            BigDecimal workAgeSchedulingTime = BigDecimal.ZERO;
            if (combinedGrant.getCompanyResultConfig() != null) {
                companySchedulingTime = getGrantBalanceByBreakpointsConfig(combinedGrant.getCompanyResultConfig(), companyAge).getData();
            }
            if (combinedGrant.getWorkResultConfig() != null) {
                workAgeSchedulingTime = getGrantBalanceByBreakpointsConfig(combinedGrant.getWorkResultConfig(), workAge).getData();
            }
            if (grantRule.getGrantStandard().equals(SchedulingLeaveConstants.GrantStandardEnum.COMPANY_AGE.getType())) {
                schedulingTimeVO.setTimeLength(companySchedulingTime);
            } else if (grantRule.getGrantStandard().equals(SchedulingLeaveConstants.GrantStandardEnum.WORKING_AGE.getType())) {
                schedulingTimeVO.setTimeLength(workAgeSchedulingTime);
            } else if (grantRule.getGrantStandard().equals(SchedulingLeaveConstants.GrantStandardEnum.MAX_AGE.getType())) {
                if (companySchedulingTime.compareTo(workAgeSchedulingTime) > 0) {
                    schedulingTimeVO.setTimeLength(companySchedulingTime);
                } else {
                    schedulingTimeVO.setTimeLength(workAgeSchedulingTime);
                }
            } else if (grantRule.getGrantStandard().equals(SchedulingLeaveConstants.GrantStandardEnum.SUM_AGE.getType())) {
                schedulingTimeVO.setTimeLength(companySchedulingTime.add(workAgeSchedulingTime));
            }
            //1.4如果有最大值限制 并且超过了最大值 则直接赋值为最大值
            if (grantRule.getCombinedGrant().getTotalLimit() != null && schedulingTimeVO.getTimeLength().compareTo(BigDecimal.valueOf(grantRule.getCombinedGrant().getTotalLimit())) > 0) {
                schedulingTimeVO.setTimeLength(BigDecimal.valueOf(grantRule.getCombinedGrant().getTotalLimit()));
            }
        }

        //2.1判断新员工在当年是否折扣
        //2.2如果是今年入职的 说明说新员工
        //2.3并且新员工打折扣
        if (DateUtil.year(now) == DateUtil.year(joinedDate) && SystemConstants.YES.equals(grantRule.getNewEmployeeDiscount())) {
            //2.3.1今年过了多少天
            long l = DateUtil.betweenDay(DateUtil.beginOfYear(now), now, true);
            int i = DateUtil.dayOfYear(now);
            //2.3.2乘以 剩余天数和总天数的比 就是应该发放的数量
            BigDecimal divide = BigDecimal.valueOf(i - l).divide(BigDecimal.valueOf(i), 2, RoundingMode.HALF_UP);
            //2.3.3有小数时取整或者不取整
            SchedulingLeaveConstants.RoundingRuleEnum byType = SchedulingLeaveConstants.RoundingRuleEnum.getByType(grantRule.getRoundingRule());
            if (byType != null) {
                BigDecimal roundValue = SchedulingLeaveConstants.RoundingRuleEnum.getRoundValue(byType, schedulingTimeVO.getTimeLength().multiply(divide));
                schedulingTimeVO.setTimeLength(roundValue);
            }
        }

        return Response.<SchedulingTimeVO>ok().wrap(schedulingTimeVO);
    }

    /**
     * 达到门槛后可以发放的数值
     *
     * @param breakpointsConfig 门槛列表,达到门槛后可以发放的数值
     * @param age               工龄或者司龄
     * @return 发放时长, 单位天
     */
    public Response<BigDecimal> getGrantBalanceByBreakpointsConfig(SchedulingLeaveConfigDto.BreakpointsConfig breakpointsConfig, long age) {

        if (CollectionUtils.isEmpty(breakpointsConfig.getBreakpoints())) {
            return Response.<BigDecimal>ok().wrap(BigDecimal.ZERO);
        }
        List<SchedulingLeaveConfigDto.Breakpoint> breakpoints = breakpointsConfig.getBreakpoints();

        //1.将门槛提取成为list
        List<Integer> collectThreshold = breakpoints.stream().map(SchedulingLeaveConfigDto.Breakpoint::getThreshold).sorted(Comparator.comparingInt(Integer::intValue)).collect(Collectors.toList());


        //2.判断age插入位置就是达到的门槛
        int i = searchInsert(collectThreshold, age);

        List<SchedulingLeaveConfigDto.Breakpoint> breakpointList = breakpoints.stream().sorted(Comparator.comparingInt(SchedulingLeaveConfigDto.Breakpoint::getThreshold)).collect(Collectors.toList());
        //3.如果查过最大的门槛值 则直接取最大值
        if (i > breakpointList.size() - 1) {
            return Response.<BigDecimal>ok().wrap(BigDecimal.valueOf(breakpointsConfig.getMaxResult()));
        }

        return Response.<BigDecimal>ok().wrap(BigDecimal.valueOf(breakpointList.get(i).getResult()));
    }

    public static void main(String[] args) {

        SchedulingLeaveBusiness
                schedulingLeaveBusiness = new SchedulingLeaveBusiness();

//        List<Integer> integers = new ArrayList<>();
//        integers.add(5);
//        integers.add(10);
//        integers.add(15);
//
//        for (int i = 0; i < 16; i++) {
//            int i1 = schedulingLeaveBusiness.searchInsert(integers, i);
//            System.out.println("这个值:" + i + "应该插入的位置:" + i1);
//        }

        SchedulingLeaveConfigDto.Breakpoint breakpoint = new SchedulingLeaveConfigDto.Breakpoint();
        breakpoint.setThreshold(5);
        breakpoint.setResult(2);


        SchedulingLeaveConfigDto.Breakpoint breakpoint2 = new SchedulingLeaveConfigDto.Breakpoint();
        breakpoint2.setThreshold(10);
        breakpoint2.setResult(4);


        SchedulingLeaveConfigDto.Breakpoint breakpoint3 = new SchedulingLeaveConfigDto.Breakpoint();
        breakpoint3.setThreshold(15);
        breakpoint3.setResult(6);


        SchedulingLeaveConfigDto.BreakpointsConfig breakpointsConfig = new SchedulingLeaveConfigDto.BreakpointsConfig();
        breakpointsConfig.setBreakpoints(Arrays.asList(breakpoint, breakpoint2, breakpoint3));
        breakpointsConfig.setMaxResult(7);


        Response<BigDecimal> grantBalanceByBreakpointsConfig = schedulingLeaveBusiness.getGrantBalanceByBreakpointsConfig(breakpointsConfig, 15);
        System.out.println(grantBalanceByBreakpointsConfig);


//
//        Date now = DateUtils.parse("2023-01-02", DateUtils.FormatType.SIMPLE);
//        long l = DateUtil.betweenDay(DateUtil.beginOfYear(now), now, true);
//        System.out.println(l
//        );

    }

    /**
     * 二分法查询插入的位置
     *
     * @param nums   有序列表
     * @param target 目标值
     * @return
     */
    public int searchInsert(List<Integer> nums, long target) {
        int left = 0;
        int right = nums.size() - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums.get(mid) == target) {
                return mid + 1;
            } else if (nums.get(mid) < target) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        // 如果没有找到目标值，返回目标值应该插入的位置
        return left;
    }


    /**
     * 单位换算
     *
     * @param num        换算数值
     * @param originUnit 原单位
     * @param targetUnit 目标单位
     * @return 返回换算后的数值
     */
    public BigDecimal changeUnit(BigDecimal num, Byte originUnit, Byte targetUnit) {
        if (originUnit.equals(targetUnit)) {
            return num;
        } else {
            SchedulingLeaveConstants.TimeUnitEnum originUnitEnum = SchedulingLeaveConstants.TimeUnitEnum.getByUnit(originUnit);
            SchedulingLeaveConstants.TimeUnitEnum targetUnitEnum = SchedulingLeaveConstants.TimeUnitEnum.getByUnit(targetUnit);
            if (originUnitEnum == null || targetUnitEnum == null) {
                return null;
            } else {
                return num.multiply(BigDecimal.valueOf(originUnitEnum.getMillis())).divide(BigDecimal.valueOf(targetUnitEnum.getMillis()), SchedulingLeaveConstants.DEFAULT_SCALE, RoundingMode.HALF_UP);
            }
        }
    }


    /**
     * 计算余额分别扣减多少
     *
     * @param balanceDetail 具体假期余额
     * @param leaveTime     请假时长
     * @return 请假余额
     */

    public Response<SchedulingTimeVO> calLeaveBalance(SchedulingTimeVO balanceDetail, SchedulingTimeVO leaveTime) {
        //对时间进行扣减 先扣除快过期的余额
        List<SchedulingBalanceTimeVO> collect = balanceDetail.getBalanceTimes().stream().sorted(Comparator.comparing(o -> o.getSchedulingLeaveBalance().getEndTime())).collect(Collectors.toList());

        BigDecimal numMills = leaveTime.getMills();
        List<SchedulingBalanceTimeVO> schedulingBalanceTimeVOS = new ArrayList<>();
        //排在前面的优先扣减
        for (SchedulingBalanceTimeVO schedulingBalanceTimeVO : collect) {
            SchedulingBalanceTimeVO applyTimeVO = CopyUtils.copyObj(schedulingBalanceTimeVO, SchedulingBalanceTimeVO.class);
            //看余额是否充足,如果充足只需要扣减一个
            if (schedulingBalanceTimeVO.getRestBalanceMills().compareTo(numMills) >= 0) {
                //换算单位 统一换算成schedulingBalanceTimeVO单位,这样方便到时候扣除
                //换算之后的单位
                BigDecimal result = changeUnit(leaveTime.getTimeLength(), leaveTime.getTimeUnit(), schedulingBalanceTimeVO.getUnit());
                if (result == null) {
                    return Response.error("换算单位出错");
                }
                applyTimeVO.setAppliedBalance(result);
                applyTimeVO.setUnit(schedulingBalanceTimeVO.getUnit());
                schedulingBalanceTimeVOS.add(applyTimeVO);
                break;
            } else {
                //先扣减一部分 循环扣除
                numMills = leaveTime.getMills().subtract(schedulingBalanceTimeVO.getRestBalanceMills());
                applyTimeVO.setAppliedBalance(applyTimeVO.getRestBalance());
                schedulingBalanceTimeVOS.add(schedulingBalanceTimeVO);
            }
        }
        leaveTime.setBalanceTimes(schedulingBalanceTimeVOS);
        return Response.<SchedulingTimeVO>ok().wrap(leaveTime);
    }


    /**
     * 获得时间段内可用的余额
     *
     * @param leaveId   假期id
     * @param personId  人员id
     * @param startTime 申请开始时间
     * @param endTime   申请技术时间
     * @return 假期余额列表
     */
    public List<SchedulingLeaveBalanceDto> getEffectiveLeaveBalance(Long leaveId, Long personId, String startTime, String endTime) {
        Map<String, Object> params = new HashMap<>(4);
        params.put(LambdaUtil.getFieldName(SchedulingLeaveBalanceDto::getLeaveId), leaveId);
        params.put(LambdaUtil.getFieldName(SchedulingLeaveBalanceDto::getPersonId), personId);
        params.put(SchedulingLeaveConstants.QUERY_START_TIME, startTime);
        params.put(SchedulingLeaveConstants.QUERY_END_TIME, endTime);

        List<SchedulingLeaveBalanceDto> schedulingLeaveBalanceDtos = schedulingLeaveBalanceService.queryList(params);
        return schedulingLeaveBalanceDtos;
    }


    /**
     * 获取假期余额
     *
     * @param leaveId   假期Id
     * @param personId  人员id
     * @param startTime 申请开始时间
     * @param endTime   申请结束时间
     * @return 合并后的假期余额
     */
    public Response<SchedulingTimeVO> getLeaveBalance(Long leaveId, Long personId, String startTime, String endTime) {
        SchedulingTimeVO schedulingTimeVO = new SchedulingTimeVO();
        schedulingTimeVO.setTimeLength(BigDecimal.ZERO);
        schedulingTimeVO.setTimeUnit(SchedulingLeaveConstants.TimeUnitEnum.HOUR.getUnit());

        SchedulingLeaveConfigDto schedulingLeaveConfigDto = schedulingLeaveConfigService.selectCacheById(leaveId);
        Response response = checkLeaveConfig(schedulingLeaveConfigDto);
        if (!response.isSuccess()) {
            return Response.error(response.getMessage());
        }

        SchedulingLeaveConstants.TimeUnitEnum leaveMinUnit = SchedulingLeaveConstants.TimeUnitEnum.getByUnit(schedulingLeaveConfigDto.getMinUnit());
        Preconditions.checkNotNull(leaveMinUnit);
        //1.单位和假期最小单位一致
        schedulingTimeVO.setTimeUnit(schedulingLeaveConfigDto.getMinUnit());
        //2.如果是不限额的 则余额为0
        if (SystemConstants.NO.equals(schedulingLeaveConfigDto.getLimitFlag())) {
            return Response.<SchedulingTimeVO>ok().wrap(schedulingTimeVO);
        }

        List<SchedulingLeaveBalanceDto> schedulingLeaveBalanceDtos = getEffectiveLeaveBalance(leaveId, personId, startTime, endTime);

        if (CollectionUtils.isEmpty(schedulingLeaveBalanceDtos)) {
            Response.<SchedulingTimeVO>ok().wrap(schedulingTimeVO);
        }
        List<SchedulingBalanceTimeVO> schedulingBalanceTimeVOS = new ArrayList<>();
        //3.合并余额
        BigDecimal totalBalance = BigDecimal.ZERO;
        for (SchedulingLeaveBalanceDto schedulingLeaveBalanceDto : schedulingLeaveBalanceDtos) {
            Byte unit = schedulingLeaveBalanceDto.getUnit();
            SchedulingLeaveConstants.TimeUnitEnum byUnit = SchedulingLeaveConstants.TimeUnitEnum.getByUnit(unit);
            if (byUnit == null) {
                return Response.error("余额配置有误，请联系管理员");
            }
            totalBalance = schedulingLeaveBalanceDto.getBalance().subtract(schedulingLeaveBalanceDto.getUsedBalance()).multiply(BigDecimal.valueOf(byUnit.getMillis()));

            SchedulingBalanceTimeVO schedulingBalanceTimeVO = new SchedulingBalanceTimeVO();
            schedulingBalanceTimeVO.setBalanceId(schedulingLeaveBalanceDto.getId());
            schedulingBalanceTimeVO.setRestBalance(schedulingLeaveBalanceDto.getBalance().subtract(schedulingLeaveBalanceDto.getUsedBalance()));
            schedulingBalanceTimeVO.setAppliedBalance(BigDecimal.ZERO);
            schedulingBalanceTimeVO.setUnit(schedulingLeaveBalanceDto.getUnit());
            schedulingBalanceTimeVO.setSchedulingLeaveBalance(schedulingLeaveBalanceDto);

            schedulingBalanceTimeVOS.add(schedulingBalanceTimeVO);
        }

        //4.总共的毫秒数
        totalBalance = totalBalance.divide(BigDecimal.valueOf(leaveMinUnit.getMillis()), SchedulingLeaveConstants.DEFAULT_SCALE, RoundingMode.HALF_UP);
        schedulingTimeVO.setTimeLength(totalBalance);
        //5.假期余额详情
        schedulingTimeVO.setBalanceTimes(schedulingBalanceTimeVOS);

        return Response.<SchedulingTimeVO>ok().wrap(schedulingTimeVO);
    }

    /**
     * 校验请假申请
     *
     * @param leaveId     假期id
     * @param personId    申请人id
     * @param startTime   申请开始时间
     * @param endTime     申请结束时间
     * @param reason      请假原因
     * @param extFileFlag 附件标识,1有附件,0没有附件
     * @return 校验结果
     */
    public Response checkLeaveApplication(Long leaveId,
                                          Long personId,
                                          Date startTime,
                                          Date endTime,
                                          String reason,
                                          Byte extFileFlag) {


        if (leaveId == null || personId == null || startTime == null || endTime == null || StringUtils.isBlank(reason) || extFileFlag == null) {
            return Response.error("缺少校验必要参数");
        }

        SchedulingLeaveConfigDto leaveDto = schedulingLeaveConfigService.selectById(leaveId);
        //1.假期配置校验
        Response response = checkLeaveConfig(leaveDto);
        if (!response.isSuccess()) {
            return response;
        }
        //2.获取请假时长
        SchedulingTimeVO leaveApplicationTimeLength = getLeaveApplicationTimeLength(
                leaveDto.getMinUnit(),
                leaveDto.getCalTimeLengthType(),
                startTime,
                endTime
        );

        BigDecimal leaveTimeLength = null;
        if (leaveApplicationTimeLength != null) {
            leaveTimeLength = leaveApplicationTimeLength.getTimeLength();
        }
        if (leaveTimeLength == null || leaveTimeLength.compareTo(BigDecimal.ZERO) < 0) {
            return Response.error("申请时间范围不合法");
        }
        //3.必须满足最小单位要求
        if (leaveTimeLength.compareTo(BigDecimal.ONE) < 0) {
            return Response.error("不足最小申请时长，请调整申请时间");
        }


        BigDecimal balance = null;
        //4.查询假期余额
        Response<SchedulingTimeVO> leaveBalanceResponse = getLeaveBalance(
                leaveId,
                personId,
                DateUtils.format(startTime, DateUtils.FormatType.NORM_DATETIME_MINUTE_FORMAT),
                DateUtils.format(endTime, DateUtils.FormatType.NORM_DATETIME_MINUTE_FORMAT));

        if (!leaveBalanceResponse.isSuccess() || leaveBalanceResponse.getData() == null) {
            return Response.error(leaveBalanceResponse.getMessage());
        } else {
            balance = leaveBalanceResponse.getData().getTimeLength();
        }

        //5.余额是否足够
        if (balance == null || leaveTimeLength.compareTo(balance) > 0) {
            return Response.error("假期余额不足");
        }

        SchedulingLeaveConfigDto.UsageRule usageRule = leaveDto.getUsageRule();
        //6.是否需要证明材料
        if (SystemConstants.YES.equals(usageRule.getEvidenceFlag()) && SystemConstants.NO.equals(extFileFlag)) {
            return Response.error("需要提供证明材料");
        }
        //7.单次最小请假时长
        if (usageRule.getSingleMinTime() != null
                && SystemConstants.YES.equals(usageRule.getSingleMinTimeLimit())
                && usageRule.getSingleMinTime().getNum() != null
        ) {
            //7.1限制最小时长 如果比最小时长还小 则不允许提交
            if (SchedulingLeaveConstants.TimeLimitTypeEnum.FIX.getType().equals(usageRule.getSingleMinTimeLimit())
                    && usageRule.getSingleMinTime().getNum().compareTo(leaveTimeLength) > 0
            ) {
                return Response.error("不满足请假最小时长要求");
            }
        }
        //8.单次最大请假时长
        if (usageRule.getSingleMaxTime() != null
                && SystemConstants.YES.equals(usageRule.getSingleMinTimeLimit())
                && usageRule.getSingleMaxTime().getNum() != null
        ) {
            //8.1限制最小时长 如果比最小时长还小 则不允许提交
            if (SchedulingLeaveConstants.TimeLimitTypeEnum.FIX.getType().equals(usageRule.getSingleMaxTimeLimit())
                    && usageRule.getSingleMaxTime().getNum().compareTo(leaveTimeLength) < 0
            ) {
                return Response.error("不满足请假最大时长要求");
            }
        }


        //9.累计请假最大时长
        if (SystemConstants.YES.equals(usageRule.getAccumulatedMaxTimeLengthLimit()) && CollectionUtils.isNotEmpty(usageRule.getAccumulatedMaxTimeLength())) {
            //9.1限制是周期
            for (SchedulingLeaveConfigDto.TimeLimitConfig timeLimitConfig : usageRule.getAccumulatedMaxTimeLength()) {

                BigDecimal accumulatedTimeLength = BigDecimal.ZERO;

                DateTime startDate = null;
                DateTime endDate = null;
                //9.1.1如果是日的周期 不能大于总时长
                if (SchedulingLeaveConstants.TimeLimitTypeEnum.PERIOD.getType().equals(timeLimitConfig.getPeriodUnit())
                        && timeLimitConfig.getNum() != null
                        && SchedulingLeaveConstants.PeriodTimeUnitEnum.DAY.getUnit().equals(timeLimitConfig.getPeriodUnit())
                ) {
                    startDate = DateUtil.beginOfDay(startTime);
                    endDate = DateUtil.endOfDay(startTime);
                }
                if (SchedulingLeaveConstants.TimeLimitTypeEnum.PERIOD.getType().equals(timeLimitConfig.getPeriodUnit())
                        && timeLimitConfig.getNum() != null
                        && SchedulingLeaveConstants.PeriodTimeUnitEnum.WEEK.getUnit().equals(timeLimitConfig.getPeriodUnit())
                ) {
                     startDate = DateUtil.beginOfWeek(startTime);
                     endDate = DateUtil.endOfWeek(startTime);
                }
                //9.1.1如果是月份周期 不能大于总时长
                if (SchedulingLeaveConstants.TimeLimitTypeEnum.PERIOD.getType().equals(timeLimitConfig.getPeriodUnit())
                        && timeLimitConfig.getNum() != null
                        && SchedulingLeaveConstants.PeriodTimeUnitEnum.MONTH.getUnit().equals(timeLimitConfig.getPeriodUnit())
                ) {
                    startDate = DateUtil.beginOfDay(startTime);
                    endDate = DateUtil.endOfMonth(startTime);
                }

                //9.2.1如果是年周期 不能大于总时长
                if (SchedulingLeaveConstants.TimeLimitTypeEnum.PERIOD.getType().equals(timeLimitConfig.getPeriodUnit())
                        && timeLimitConfig.getNum() != null
                        && SchedulingLeaveConstants.PeriodTimeUnitEnum.YEAR.getUnit().equals(timeLimitConfig.getPeriodUnit())
                ) {
                    startDate = DateUtil.beginOfYear(startTime);
                    endDate = DateUtil.endOfYear(startTime);
                }
                List<SchedulingApplyLeaveRecordDto> leaveRecords = getLeaveRecord(
                        leaveDto.getTenantId(),
                        leaveDto.getVillageId(),
                        leaveDto.getId(),
                        personId,
                        DateUtils.format(startDate, DateUtils.FormatType.COMMON),
                        DateUtils.format(endDate, DateUtils.FormatType.COMMON)
                );
                if (CollectionUtils.isNotEmpty(leaveRecords)) {
                    BigDecimal reduce = leaveRecords.stream().map(schedulingApplyLeaveRecordDto -> {
                        if (schedulingApplyLeaveRecordDto.getTimeLength() == null) {
                            schedulingApplyLeaveRecordDto.setTimeLength(BigDecimal.ZERO);
                        }
                        return SchedulingLeaveConstants.TimeUnitEnum.changeUnit(schedulingApplyLeaveRecordDto.getTimeLength(), schedulingApplyLeaveRecordDto.getTimeUnit(), timeLimitConfig.getUnit());
                    }).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
                    accumulatedTimeLength = accumulatedTimeLength.add(reduce);
                }
                //加上申请时长是否超过限制
                accumulatedTimeLength = accumulatedTimeLength.add(leaveApplicationTimeLength.getTimeLength());
                if (accumulatedTimeLength.compareTo(timeLimitConfig.getNum()) < 0) {
                    return Response.error("不满足最大限额要求");
                }

            }
        }
        //10.提交申请时间限制
        if (usageRule.getSubmitTimeLimit() != null && usageRule.getSubmitTimeLimit().getNum() != null) {
            if (SchedulingLeaveConstants.SubmitTimeLimitEnum.BEFORE_START_TIME.getType().equals(usageRule.getSubmitTimeLimitType())) {
                //10.1如果开始时间晚于这个时间则不让申请
                DateTime startLimitTime = DateUtil.offsetDay(startTime, -usageRule.getSubmitTimeLimit().getNum().intValue());
                if (startLimitTime.before(new Date())) {
                    return Response.error("不满足提交申请时间限制，需要在" + startLimitTime + "之前申请或者调整申请时间");
                }

            } else if (SchedulingLeaveConstants.SubmitTimeLimitEnum.AFTER_END_TIME.getType().equals(usageRule.getSubmitTimeLimitType())) {
                //10.2如果开始时间晚于这个时间则不让申请
                DateTime endLimitTime = DateUtil.offsetDay(endTime, usageRule.getSubmitTimeLimit().getNum().intValue());
                if (endLimitTime.before(new Date())) {
                    return Response.error("不满足提交申请时间限制，需要在" + endLimitTime + "之前申请或者调整申请时间");
                }
            }
        }
        //11.申请资格限制
        if (usageRule.getQualificationLimit() != null
                && SystemConstants.YES.equals(usageRule.getQualificationLimitType())
                && usageRule.getQualificationLimit().getNum() != null
        ) {
            DeptPersonDto deptPersonDto = deptPersonService.selectCacheById(personId);
            if (deptPersonDto == null) {
                return Response.error("员工不存在");
            }
            if (deptPersonDto.getJoinedDate() == null) {
                return Response.error("入职日期为空，请联系管理员补全信息后再次尝试");
            }

            //11.1必须在入职时间过后才可以申请
            long l = DateUtil.betweenDay(new Date(), deptPersonDto.getJoinedDate(), true);
            //11.2如果入职时间间隔小于限制则不允许提交
            if (BigDecimal.valueOf(l).compareTo(usageRule.getQualificationLimit().getNum()) < 0) {
                return Response.error("入职" + usageRule.getQualificationLimit().getNum() + "才可以申请");
            }
        }
        return Response.ok();
    }

    /**
     * 获取请假时长
     *
     * @param timeUnit               请假时长单位
     * @param calLeaveTimeLengthType 计算请假时长的方式 1按工作日计算请假时长2按自然日计算请假时长
     * @param startTime              申请开始时间
     * @param endTime                申请结束时间
     * @return 返回请假时长
     */
    public SchedulingTimeVO getLeaveApplicationTimeLength(
            Byte timeUnit,
            Byte calLeaveTimeLengthType,
            Date startTime,
            Date endTime
    ) {
        Preconditions.checkNotNull(timeUnit);
        Preconditions.checkNotNull(calLeaveTimeLengthType);
        Preconditions.checkNotNull(startTime);
        Preconditions.checkNotNull(endTime);

        SchedulingLeaveConstants.TimeUnitEnum byUnit = SchedulingLeaveConstants.TimeUnitEnum.getByUnit(timeUnit);
        SchedulingLeaveConstants.CalLeaveTimeLenghTypeEnum byType = SchedulingLeaveConstants.CalLeaveTimeLenghTypeEnum.getByType(calLeaveTimeLengthType);

        Preconditions.checkNotNull(byUnit);
        Preconditions.checkNotNull(byType);

        return getLeaveApplicationTimeLength(byUnit, byType, startTime, endTime);
    }


    /**
     * 获取请假时长
     *
     * @param timeUnitEnum               时间单位枚举值
     * @param calLeaveTimeLengthTypeEnum 计算方式枚举值
     * @param startTime                  申请开始时间
     * @param endTime                    申请结束时间
     * @return 返回时间长度
     */
    public SchedulingTimeVO getLeaveApplicationTimeLength(
            SchedulingLeaveConstants.TimeUnitEnum timeUnitEnum,
            SchedulingLeaveConstants.CalLeaveTimeLenghTypeEnum calLeaveTimeLengthTypeEnum,
            Date startTime,
            Date endTime
    ) {

        SchedulingTimeVO schedulingTimeVO = new SchedulingTimeVO();
        schedulingTimeVO.setTimeLength(BigDecimal.ZERO);
        schedulingTimeVO.setTimeUnit(timeUnitEnum.getUnit());

        long diff = endTime.getTime() - startTime.getTime();

        //1.如果是自然日
        if (calLeaveTimeLengthTypeEnum.equals(SchedulingLeaveConstants.CalLeaveTimeLenghTypeEnum.NATURE_DAY)) {
            //1.1换算单位
            schedulingTimeVO.setTimeLength(BigDecimal.valueOf(diff).divide(BigDecimal.valueOf(timeUnitEnum.getMillis()), SchedulingLeaveConstants.DEFAULT_SCALE, RoundingMode.HALF_UP));
        } else if (calLeaveTimeLengthTypeEnum.equals(SchedulingLeaveConstants.CalLeaveTimeLenghTypeEnum.WORK_DAY)) {
            //2.如果是工作日计算 时长 = 自然日时长 - 节假日时长 - 休息日时长
            //TODO 时间段中间的工作日,节假日和休息日
            long mills = diff;
            schedulingTimeVO.setTimeLength(BigDecimal.valueOf(mills).divide(BigDecimal.valueOf(timeUnitEnum.getMillis()), SchedulingLeaveConstants.DEFAULT_SCALE, RoundingMode.HALF_UP));
        }
        return schedulingTimeVO;
    }


}
